﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using TCCHWLib.Commands;
using TCCHWLib;
using TCCSWLib;


namespace TCCCompiler
{
    class command_generator
    {
        TreeNode Root;
        TreeNode Data;
        bool atomic = false;  // to know that i am inside atomic 
        bool t_for = false;
        bool is_arr = true;
        string t_for_index;
        bool is_data=false;   // to know i am using write command not txwrite
        static int phase; //the pariority part
        TreeNode Current_Node;
        bool stmnt=false;
        public List<ICommand> commands = new List<ICommand>();  //to put the commands that is inside the current block to deallocate it at the end
        BatchCommand current_batch;     // the current batch command that iam adding in 
        public Dictionary<BatchCommand,PriorityKey> batches = new Dictionary<BatchCommand,PriorityKey>();
        
        public command_generator(TreeNode node,TreeNode data)
        {
            Data = data;
            Root = node;
            phase = 1;
        }
        public void write_data()
        {
            is_data = true;
            current_batch = new BatchCommand();
            foreach (TreeNode node1 in Data.Nodes)
            {
                Generate_Commands(node1);
            }
            is_data = false;
           
            JobScheduler.Instance.AddIntialData(current_batch);
            current_batch = null;
        }
        private bool expect(string str)
        {
            if (Current_Node.Text == str)
                return true;
            else
                return false;
        }

        private SimpleExpression resolve_exp(TreeNode Node)
        {
            Current_Node = Node;
            SimpleExpression exp = new SimpleExpression(Current_Node,current_batch,t_for,t_for_index,is_arr);
            current_batch = exp.Current_Batch;
            return exp;
        }
        private void resolve_condition(TreeNode node)
        {
            Current_Node = node;
            SimpleCondition condition = new SimpleCondition(Current_Node,current_batch,t_for,t_for_index,is_arr);
        }
        private  void resolve_stmnt(TreeNode node)
        {
            foreach (TreeNode node1 in node.Nodes)
            {
                stmnt = true;
                Generate_Commands(node1);
            }
        }
        private void If_stmnt(TreeNode Node)
        {
            int condition_value,if_finished;
            IF_Command IF = new IF_Command();

            if (Node.Nodes[0].Text != "Reserved_Word")
                return ;

            Current_Node = Node.Nodes[1];

            if (expect("Condition"))
            {
                Current_Node = Current_Node.Nodes[0];
                resolve_condition(Current_Node);
                condition_value = current_batch.CommandList.Count - 1;
                IF.Operand1 = condition_value;
                IF.Type1 = ParameterType.Instruction;
            }

            else
                return;

            current_batch.AddCommand(IF);
            Current_Node=Node.Nodes[2];
            if (expect("Sequence_Of_Statements"))
            {
                stmnt = true;
                resolve_stmnt(Current_Node);
                
                //deallocate the variables in the "if" scope

                for (int i = 0; i < commands.Count; i++)
                {
                    DeAllocateCommand deAllocate = new DeAllocateCommand(commands[i].Operand1, ParameterType.Variable);
                    current_batch.AddCommand(deAllocate);

                }
                commands = new List<ICommand>();
                stmnt = false;
                if_finished = current_batch.CommandList.Count - 1;
            }
            else
                return ;
            
            if (Node.Nodes.Count <= 3)
            {
                IF.Operand2 = current_batch.CommandList.Count;
                IF.Type2 = ParameterType.Value;
                current_batch.CommandList[if_finished].NextPC = current_batch.CommandList.Count;
                return;
            }
            Current_Node=Node.Nodes[3];
            if (expect("Reserved_Word"))
            {
                Current_Node = Node.Nodes[4];
                if (expect("Sequence_Of_Statements"))
                {
                    //for deallocation
                    stmnt = true;

                    IF.Operand2=current_batch.CommandList.Count;
                    IF.Type2 = ParameterType.Value;
                    resolve_stmnt(Current_Node);

                    //deallocate the variables in the "else" scope

                    for (int i = 0; i < commands.Count; i++)
                    {
                        DeAllocateCommand deAllocate = new DeAllocateCommand(commands[i].Operand1, ParameterType.Variable);
                        current_batch.AddCommand(deAllocate);

                    }
                    commands = new List<ICommand>();
                    stmnt = false;
                    //End_pc = current_batch.CommandList.Count;
                    
                }
                else
                    return;
                current_batch.CommandList[if_finished].NextPC = current_batch.CommandList.Count;
            }
            
           
           
            
            
        }
        public void resolve_Declaration(TreeNode node)
        {
            bool array = false;
            string type;
            string parameter="";
            object size=1;
            ParameterType size_type=ParameterType.Value;
            Current_Node = node.Nodes[0];
            // el type
            if (expect("Reserved_Word"))
            {
                type = Current_Node.Nodes[0].Text;
            }
            Current_Node = Current_Node.NextNode;
            // check if it is array
            if (expect("Operator"))
            {
                if (Current_Node.Nodes[0].Text == "[")
                {
                    Current_Node = node.Nodes[3];
                    array = true;
                }
            }
            // get the name of the variable

            if (expect("Identifier"))
                parameter = Current_Node.Nodes[0].Text;
            Current_Node = Current_Node.NextNode;
            // check if it is a single variable without assignmnet OR array with =new OR single variable with assignmnet

            if(Current_Node.Text=="Operator")
            {
                // single variable with assignmet
                if (Current_Node.Nodes[0].Text == "="&& !array)
                {
                    TreeNode assignmet=(TreeNode)node.Clone();
                    assignmet.Nodes[0].Remove();
                    AllocateCommand allocate=new AllocateCommand(parameter,ParameterType.Value,size,size_type);
                    current_batch.AddCommand(allocate);
                    ////////////////////////////////
                    //for deallocation
                    if (stmnt)
                        commands.Add(allocate);
                    
                   
                    /////////////////////////////////////////////
                    resolve_Assignment(assignmet);
                }
                else
                    //Array with "=new"
                    if (Current_Node.Nodes[0].Text == "="&& array)
                    {
                        Current_Node=node.Nodes[8];
                        if (expect("Expression"))
                        {
                            Current_Node = Current_Node.Nodes[0];
                            SimpleExpression exp=resolve_exp(Current_Node);
                            size=exp.Value;
                            size_type=exp.Type;
                        }
                        AllocateCommand allocate=new AllocateCommand(parameter,ParameterType.Value,size,size_type);
                        current_batch.AddCommand(allocate);
                        ////////////////////////////////
                        //for deallocation
                        if (stmnt)
                            commands.Add(allocate);
                        
                        /////////////////////////////////////////////
                    }
                else
                        //single variable without assignment
                        if (Current_Node.Nodes[0].Text == ";")
                        {
                            AllocateCommand allocate = new AllocateCommand(parameter, ParameterType.Value, size, size_type);
                            current_batch.AddCommand(allocate);
                            ////////////////////////////////
                            //for deallocation
                            if (stmnt)
                                commands.Add(allocate);
                            
                            /////////////////////////////////////////////
                        }
            }
        }
        
        public void resolve_Assignment(TreeNode node)
        {
            string parameter_1=null;
            string param_read=null;
            object read_size=0;
            ParameterType read_size_type=ParameterType.Value;
            object value=null;
            ParameterType value_type=ParameterType.Value;
            object size =0;
            ParameterType size_type=ParameterType.Null;
            SimpleExpression exp;

            //start with identifier guaranteed
            Current_Node = node.Nodes[0];
            if (expect("Identifier"))
                parameter_1 = Current_Node.Nodes[0].Text;
            Current_Node=node.Nodes[1];
            //check if it is an array 
           
            
            if (expect("Operator"))
            {
                //if it is not array go to after the asignment operator
                if(Current_Node.Nodes[0].Text=="=")
                    Current_Node = node.Nodes[2];
                else
                    //if it  is an array get the index
                    if(Current_Node.Nodes[0].Text=="[")
                    {
                        Current_Node=node.Nodes[2];
                        if(expect("Expression"))
                        {
                            Current_Node = Current_Node.Nodes[0];
                             is_arr = true;
                            exp=resolve_exp(Current_Node);
                            is_arr = false;
                            size=exp.Value;
                            size_type=exp.Type;
                            //go to after the assignment operator
                            Current_Node=node.Nodes[5];
                        }
                    }
            }
            // the term after the assignment operator is either exp or identifier or constant 
            if (expect("Expression"))
            {
                Current_Node = Current_Node.Nodes[0];
                exp=resolve_exp(Current_Node);
                current_batch = exp.Current_Batch;
                value=exp.Value;
                value_type=exp.Type;
                //found the value of the exp go and write it.

            }
            else
                if (expect("Constant"))
                {
                     string constant = Current_Node.Nodes[0].Text;
                    constant= constant.Substring(1, constant.Length - 2);
                     
                    value = constant;
                    value_type = ParameterType.Value;

                }
                else
                    if (expect("Identifier"))
                    {
                        //if it is identifier then read it first
                        param_read = Current_Node.Nodes[0].Text;
                        Current_Node = Current_Node.NextNode;
                        //Current_Node = node.Nodes[6];
                        if (expect("Operator"))
                        {
                            // then the identifier was a variable
                            if (Current_Node.Nodes[0].Text == ";")
                            {
                                read_size = 0;
                                read_size_type = ParameterType.Null;

                            }
                            else
                            //the identifier was an araay
                            {
                                // Current_Node = node.Nodes[7];
                                Current_Node = Current_Node.NextNode;
                                if (expect("Expression"))
                                {
                                    Current_Node= Current_Node.Nodes[0];
                                    is_arr = true;
                                    exp = resolve_exp(Current_Node);
                                    is_arr = false;
                                    read_size = exp.Value;
                                    read_size_type = exp.Type;
                                }
                                TxReadCommand read = new TxReadCommand(param_read, ParameterType.Variable, read_size, read_size_type);
                                current_batch.AddCommand(read);
                                value = current_batch.CommandList.Count - 1;
                                value_type = ParameterType.Instruction;
                            }
                        }
                    }

            if (!is_data)
            {
                TxWriteCommand txwrite = new TxWriteCommand(parameter_1, ParameterType.Variable, value, value_type, size, size_type);
                current_batch.AddCommand(txwrite);
            }
            else
            {
                WriteCommand write = new WriteCommand(parameter_1, ParameterType.Variable, value, value_type, size, size_type);
                current_batch.AddCommand(write);
            }
        }
        public void resolve_atomic(TreeNode node)
        {
            //for deallocation
            stmnt = true;

            if ( current_batch!= null)
            {
                TXCommitCommand commit = new TXCommitCommand();
                current_batch.AddCommand(commit);
                PriorityKey priority = new PriorityKey(phase, 0);
                batches.Add(current_batch,priority);
                phase++;
            }
            current_batch = new BatchCommand();
            TXBeginCommand begin = new TXBeginCommand();
            current_batch.AddCommand(begin);
            Current_Node = node.Nodes[1];
            resolve_stmnt(Current_Node);

            //deallocate the variables in the "for" scope

            for (int i = 0; i < commands.Count; i++)
            {
                DeAllocateCommand deAllocate = new DeAllocateCommand(commands[i].Operand1, ParameterType.Variable);
                current_batch.AddCommand(deAllocate);

            }
            commands = new List<ICommand>();
            stmnt = false;
            //closing the batch
            TXCommitCommand commit1 = new TXCommitCommand();
            current_batch.AddCommand(commit1);
            PriorityKey priority1= new PriorityKey(phase, 0);
            batches.Add(current_batch, priority1);
            phase++;
            atomic = true;
            current_batch = null;
            
        }
        public void resolve_For(TreeNode node)
        {
            stmnt = true;
            int start_condition;
            //bool t_for_n = false;
            IF_Command IF = new IF_Command();
            Current_Node = node.Nodes[0].Nodes[0];
            switch (Current_Node.Text)
            {
                case "t_for":
                    resolve_t_for(node, true);
                    return;

                case "t_for_n":
                    resolve_t_for_n(node, true);
                    return;

                case "t_for_n_unordered":
                    resolve_t_for_n(node, false);
                    return;

                case "t_for_unordered":
                    resolve_t_for(node, false);
                    return;
                    
            }
            
            Current_Node = node.Nodes[1];
            resolve_Declaration(Current_Node);
            //the simple condition node
            Current_Node = node.Nodes[3].Nodes[0];
            start_condition = current_batch.CommandList.Count;
            resolve_condition(Current_Node);
            IF.Operand1 = current_batch.CommandList.Count-1;
            IF.Type1 = ParameterType.Instruction;
            current_batch.AddCommand(IF);
            //the sequence of statments node
            Current_Node = node.Nodes[6];
            resolve_stmnt(Current_Node);
            //the assignment statment (increment) node
            Current_Node = node.Nodes[5];
            resolve_Assignment(Current_Node);
            IF.Operand2 = current_batch.CommandList.Count ;
            IF.Type2 = ParameterType.Value;
            current_batch.CommandList[current_batch.CommandList.Count - 1].NextPC = start_condition;

            //deallocate the variables in the "for" scope

            for (int i = 0; i < commands.Count; i++)
            {
                DeAllocateCommand deAllocate = new DeAllocateCommand(commands[i].Operand1, ParameterType.Variable);
                current_batch.AddCommand(deAllocate);
                
            }
            commands = new List<ICommand>();
            stmnt = false;
           // current_batch = null;
        }
        public void resolve_t_for_n(TreeNode node,bool oredred)
        {
            
            int seq ;
            if (oredred)
                seq = 1;
            else
                seq = 0;

            Current_Node = node.Nodes[3];
            Current_Node = Current_Node.Nodes[0].Nodes[0].Nodes[2].Nodes[0].Nodes[0].Nodes[0].Nodes[0].Nodes[0];
            double no_batches = double.Parse( Current_Node.Text);
            Current_Node = node.Nodes[7];
            int n =int.Parse( Current_Node.Nodes[0].Nodes[0].Nodes[0].Nodes[0].Text);
            no_batches = no_batches / n;
            no_batches= Math.Ceiling(no_batches);
          
            if (current_batch != null)
            {
                TXCommitCommand commit = new TXCommitCommand();
                current_batch.AddCommand(commit);
                PriorityKey priority = new PriorityKey(phase, 0);
                batches.Add(current_batch, priority);
                phase++;
            }
            for (int i = 0; i < no_batches; i++)
            {
                stmnt = true;
                current_batch = new BatchCommand();
                TXBeginCommand begin = new TXBeginCommand();
                current_batch.AddCommand(begin);
                string t_for="t_for";
                AllocateCommand allocate = new AllocateCommand(t_for, ParameterType.Value, 1, ParameterType.Value);
                 current_batch.AddCommand(allocate);
                 commands.Add(allocate);
                 TxWriteCommand write = new TxWriteCommand(t_for, ParameterType.Variable, 1, ParameterType.Value, 0, ParameterType.Value);
                 current_batch.AddCommand(write);
               int start_condition = current_batch.CommandList.Count ;
                TxReadCommand read=new TxReadCommand(t_for,ParameterType.Variable,0,ParameterType.Value);
                current_batch.AddCommand(read);
                LessThanCommand lessthan=new LessThanCommand(current_batch.CommandList.Count-1,ParameterType.Instruction,n,ParameterType.Value);
                current_batch.AddCommand(lessthan);
                IF_Command IF = new IF_Command();
                IF.Operand1 = current_batch.CommandList[current_batch.CommandList.Count - 1];
                IF.Type1 = ParameterType.Instruction;
                current_batch.AddCommand(IF);
                //the sequence of statments node
                Current_Node = node.Nodes[8];
                resolve_stmnt(Current_Node);
                //the assignment statment (increment) node
                //Current_Node = node.Nodes[5];
                //resolve_Assignment(Current_Node);
                TxReadCommand read2 = new TxReadCommand(t_for, ParameterType.Variable, 0, ParameterType.Value);
                current_batch.AddCommand(read2);
                AddCommand add = new AddCommand(current_batch.CommandList.Count - 1, ParameterType.Instruction, 1, ParameterType.Value);
                current_batch.AddCommand(add);
                IF.Operand2 = current_batch.CommandList.Count;
                IF.Type2 = ParameterType.Value;
                current_batch.CommandList[current_batch.CommandList.Count - 1].NextPC = start_condition;
                for (int j = 0; j < commands.Count; j++)
                {
                    DeAllocateCommand deAllocate = new DeAllocateCommand(commands[j].Operand1, ParameterType.Variable);
                    current_batch.AddCommand(deAllocate);

                }
                commands = new List<ICommand>();
                stmnt = false;
                TXCommitCommand commit = new TXCommitCommand();
                current_batch.AddCommand(commit);
                PriorityKey priority = new PriorityKey(phase, seq);
                batches.Add(current_batch, priority);
                current_batch = null;
                if(oredred)
                    seq++;
               
            }
            phase++;
        }
        public void resolve_t_for(TreeNode node,bool ordered)
        {
            
            int seq ;
            if (ordered)
                seq = 1;
            else
                seq = 0;
            Current_Node = node.Nodes[1];
            Current_Node = Current_Node.Nodes[3].Nodes[0].Nodes[0].Nodes[0].Nodes[0].Nodes[0];
            int start_index = int.Parse(Current_Node.Text);
            Current_Node = node.Nodes[3];
            Current_Node = Current_Node.Nodes[0].Nodes[0].Nodes[2].Nodes[0].Nodes[0].Nodes[0].Nodes[0].Nodes[0];
            double no_batches = double.Parse(Current_Node.Text);
            no_batches = no_batches - start_index;
            if (current_batch != null)
            {
                TXCommitCommand commit = new TXCommitCommand();
                current_batch.AddCommand(commit);
                PriorityKey priority = new PriorityKey(phase, 0);
                batches.Add(current_batch, priority);
                phase++;
            }
            for (int i = 0; i < no_batches; i++)
            {
                stmnt = true;
                string index="index "+ i.ToString();
                current_batch = new BatchCommand();
                TXBeginCommand begin = new TXBeginCommand();
                current_batch.AddCommand(begin);
                AllocateCommand allocate = new AllocateCommand(index.ToString(), ParameterType.Value, 1, ParameterType.Value);
                current_batch.AddCommand(allocate);
                commands.Add(allocate);
                TxWriteCommand write = new TxWriteCommand(index.ToString(), ParameterType.Variable, start_index, ParameterType.Value, 0, ParameterType.Value);
                current_batch.AddCommand(write);
                start_index++;
                Current_Node = node.Nodes[6];
                t_for = true;
                t_for_index = index;
                resolve_stmnt(Current_Node);
                t_for = false;
                for (int j = 0; j < commands.Count; j++)
                {
                    DeAllocateCommand deAllocate = new DeAllocateCommand(commands[j].Operand1, ParameterType.Variable);
                    current_batch.AddCommand(deAllocate);

                }
                commands = new List<ICommand>();
                stmnt = false;
                TXCommitCommand commit = new TXCommitCommand();
                current_batch.AddCommand(commit);
                PriorityKey priority = new PriorityKey(phase, seq);
                batches.Add(current_batch, priority);
                current_batch = null;
                if(ordered)
                    seq++;

            }
            phase++;
        }
        public void resolve_While(TreeNode node)
        {
            stmnt = true;
            int start_condition;
            IF_Command IF = new IF_Command();
            Current_Node = node.Nodes[0].Nodes[0];
            if (expect("t_while"))
            {
                if (current_batch != null)
                {
                    TXCommitCommand commit = new TXCommitCommand();
                    current_batch.AddCommand(commit);
                    PriorityKey priority = new PriorityKey(phase, 0);
                    batches.Add(current_batch, priority);
                    phase++;
                }
                current_batch = new BatchCommand();
                TXBeginCommand begin = new TXBeginCommand();
                current_batch.AddCommand(begin);
            }
            Current_Node = node.Nodes[1].Nodes[0];
            
            start_condition = current_batch.CommandList.Count;
            resolve_condition(Current_Node);
            IF.Operand1 = current_batch.CommandList.Count - 1;
            IF.Type1 = ParameterType.Instruction;
            current_batch.AddCommand(IF);
            //the sequence of statments node
            Current_Node = node.Nodes[2];
            resolve_stmnt(Current_Node);
            //the assignment statment (increment) node
           
            IF.Operand2 = current_batch.CommandList.Count;
            IF.Type2 = ParameterType.Value;
            current_batch.CommandList[current_batch.CommandList.Count - 1].NextPC = start_condition;

            //deallocate the variables in the "while" scope

            for (int i = 0; i < commands.Count; i++)
            {
                DeAllocateCommand deAllocate = new DeAllocateCommand(commands[i].Operand1, ParameterType.Variable);
                current_batch.AddCommand(deAllocate);

            }
            commands = new List<ICommand>();
            stmnt = false;
        }
        public void Generate_Commands(TreeNode node)
        {
                      
            switch (node.Text)
            {
                case "If_Statement":
                     If_stmnt(node);
                    break;

                case "Expression":
                    node = node.Nodes[0];
                    resolve_exp(node);                    
                    break;

                case "Condition":
                    resolve_condition(node);                  
                    break;

                case "Assignment_Statement":
                    resolve_Assignment(node);
                    break;

                case "Declaration_Statement":
                    resolve_Declaration(node);
                    break;

                case "Atomic_Block":
                    resolve_atomic(node);
                    break;

                case "For_Statement":
                    resolve_For(node);
                    break;

                case "While_Statement":
                    resolve_While(node);
                    break;

                case "lock":
                    break;

                case "unlock":
                    break;

            }
           

        }
        public Dictionary<BatchCommand,PriorityKey> start()
        {
            //write the data to the shared memory
            write_data();

            // starting to resolve the tree
            TreeNode node =(TreeNode) Root.Clone();
            if (node.Nodes[0].Text != "Atomic_Block" && node.Nodes[0].Nodes[0].Nodes[0].Text != "t_for" && node.Nodes[0].Nodes[0].Nodes[0].Text != "t_for_n" && node.Nodes[0].Nodes[0].Nodes[0].Text != "t_for_unordered" && node.Nodes[0].Nodes[0].Nodes[0].Text != "t_for_n_unordered")
            {
                //creating new batch command
                current_batch = new BatchCommand();
                TXBeginCommand TXbegin = new TXBeginCommand();
                current_batch.AddCommand(TXbegin);
            }
            foreach (TreeNode node1 in node.Nodes)
            {
                if (atomic && node1.Text != "Atomic_Block" && node1.Nodes[0].Nodes[0].Text != "t_for" && node1.Nodes[0].Nodes[0].Text != "t_for_n" && node1.Nodes[0].Nodes[0].Text != "t_for_unordered" && node1.Nodes[0].Nodes[0].Text != "t_for_n_unordered")
                {

                    current_batch = new BatchCommand();
                    TXBeginCommand TXbegin = new TXBeginCommand();
                    current_batch.AddCommand(TXbegin);
                }
                atomic = false;
                Generate_Commands(node1);
            }
            //if (!atomic)
            if(current_batch!=null)
            {
                TXCommitCommand commit = new TXCommitCommand();
                current_batch.AddCommand(commit);
                PriorityKey priority = new PriorityKey(phase, 0);
                batches.Add(current_batch, priority);
                atomic = false;
            }
            return batches;
        }

    }
}















